home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
program
/
tjgold.zip
/
INSTALL.004
/
FGUSER16.TXT
< prev
next >
Wrap
Text File
|
1995-05-29
|
76KB
|
1,781 lines
Managing Input Forms
"Will that Gold product ever get finished?"
A. Spouse, 1995
Introduction
Input/Output, or IO for short, is the heart of any program. As
you are about to discover, it is also the heart of Gold!
The Gold units which support IO forms are GOLDIO, GOLDIO2 and
GOLDIO3. These units offer a full set of sophisticated input tools
including the following:
27 different field types support string, number and date
input, along with radio buttons, checks boxes, lists and push
buttons.
Automated input validation, e.g. only accept numbers in the
range 16 to 60; only accept dates in 1995; etc.
Flexible picture fields allowing users to input telephone
numbers, part numbers, etc.
A bevy of user hooks allowing the developer to fine tune the
form's behavior.
Forms can be displayed in draggable windows.
Exotic fields such as spinners and drop-down calendars are
available.
All validation and error messages are customizable allowing
easy internationalization (that's localization for the politically
correct among us). Multiple simultaneous forms are supported. For
example, a secondary form can be displayed when a user clicks on a
button in the main form. Fully supports the mouse. All colors are
customizable. Fields can be grayed (or disabled) based on the
values in other fields. Oh, that's enough, you get the idea.
Building a Form -- A Quick Recipe
Time for an analogy. You don't need to be an accredited chef
to cook breakfast (unless you happen to be French). The same goes
for IO forms -- although there is a great deal that you need to
learn to become a Form Jedi, you can still create very useful and
functional forms by following an essentially simple recipe.
Before you delve into the form creation process, you need to
understand some of the basic principles. A form is a collection of
fields bound together to give the user a single input screen or
dialog. Fields have various uses. You might use a field to record a
person's name or zip code, allowing the end user to type text
directly into the field. Other fields don't accept normal data
entry, such as radio buttons or lists -- the user makes a choice
from a predefined set.
If you follow a simple recipe and take the steps in the
correct order, you can create a form in just a few minutes. Here is
the recipe:
Decide what data you want to garner from the user, and what
the data types will be.
e.g. 5 strings for the address, an integer for the age, and a
string for the sex.
Decide on the basic layout of the form.
e.g. the form might have 5 fields (one below the other) to
enter the user's name and address, and a numeric field to
enter the person's age. Finally, there might be OK and Cancel
buttons on the form. Activate a form.
A Gold form can support multiple forms, and at any one time, a
single form will be the active form, i.e. will have focus. All IO
procedures and function calls are directed at the active form.
Add the fields to the form.
At this stage, Gold doesn't care about each field's properties
-- just the order of the fields and the (X,Y) coordinate of each
field.
Define the properties of each field.
You might set the first field, for example, to be a string
field that accepts up to 30 alpha numeric characters. You will also
tie fields to specific variables. When the user enters data in the
field, the variable is automatically updated with the value
displayed in the field.
Add optional labels and messages to each field.
e.g. You might add the left-justified label "Name:" to the
first field .
Assign appropriate values to the variables referenced by the form.
Run the form and wait for user input.
If the form is no longer required dispose of the fields.
Figure 16.1 is the form created in DEMIO1.PAS. On the
following page is a full listing of the program along with
annotations showing each of the major steps in the recipe.
Figure 16.1
A Basic Form
Let's analyze a few lines of code from the example.
KwikAddField(1, 25,9);
KwikAddField(2, 25,11);
These lines add fields to the form. The first field is located
at coordinates (25,9) and the second field is located at
coordinates (25,11), i.e. two lines below the first line.
StringField(1, Name, '******************************');
SetLabel(1, LabelLeft,LabelLeft,'Full name:');
These two lines define the properties of field one. The field
is a string field, i.e. a standard field where the user can enter
text or numbers. The variable associated with the field is named
NAME, and this variable will be assigned the string that the user
enters. The third parameter identifies the string picture. An
asterisk "*" indicates that any alphanumeric character will be
accepted in that position. The other picture characters are "!"
which forces alphas to upper case, "#" which accepts only numbers,
and "@" which accepts letters and punctuation. In this case, the
field will be 30 characters wide because there are 30 asterisks in
the picture. The second line adds a left justified label 'Full
name:' to the field -- you can see this label in Figure 16.1.
StringField(2, Tel, '(###) ###-####');
SetLabel(2, LabelLeft,LabelLeft,'Tel:');
These two lines define the properties of field one. This field
is also a string but the picture is formatted for telephone number
input (see Figure 16.1). The tel string will be updated with the
user input. The second line adds a left justified label of 'Tel: '
to the field.
ButtonField(5,' OK ',finished);
This line sets the fifth field to be a push button with the
text OK. When the button is pressed, the form input will be
terminated and the value Finished will be returned by the EditForm
function.
Result := EditForm(1);
This line instructs Gold to display the form and wait for the
user to conclude the input session. The function returns a value of
type gAction to indicate how the user ended the session. In this
example, the function might return Finished if the F10 key was
pressed or the user selected the OK button, or Escaped if the user
pressed the Esc key or selected the Cancel button.
If you are the kind of programmer who likes to learn by
example (that's all of you, by the way), investigate the demo files
DEMIO2.PAS through DEMIO5.PAS. Each demo builds on the previous
one, and adds another degree of sophistication to the form. By the
last demo, the form is displayed in a window with field messages
displayed on the status bar, radio buttons and ... well, just take
a look for yourself.
Configuring the Form(s)
Having learned the instant form recipe, it's time to take a
more detailed look at the entire form management process. RTFM.
Creating Multiple Forms
Gold allows an application to have multiple forms defined at
one time, i.e. within the same scope. For example, form 1 might be
a customer name and address form, and form 2 might be an order
entry form. If you want to use more than one form at a time, you
must instruct Gold to create the number of forms you will need
using the following function:
CreateForms(Count:byte);
Instructs Gold to allocate enough memory for Count forms. Note
that Count must be in the range 1 to MaxForms (which is defined in
GOLDIO as 10). This should be called before any other IO procedures
and functions.
If you call any Gold IO procedure or function before you call
CreateForms, Gold will assume that you will only be using one form,
and will call CreateForms(1) internally. That is why the example on
page 17-4 didn't use a CreateForms statement.
It is important to realize that you do not need to create more
than one form just because you intend to use more than one type of
form in your application. You can use the same form for more than
one purpose if you dispose of the first set of fields and redefine
a new set of fields prior to displaying the revised form -- more
about field disposing later.
If you want to call CreateForms more than once in an
application (and this will be rare), you must ensure that the you
call DisposeForms before calling CreateForms a second time.
Giving a Form Focus
If you don't make a call to CreateForms, then you don't need
to worry about form focus. If your application creates more than
one form, read on.
The notion of focus is used in many parts of Gold: the top
window on the desktop has focus, and in list applications, a
specific single or double linked list has focus. The same goes for
input forms. If you have more than one form in an application, Gold
needs to know which form to work on, i.e. which form has focus.
The following procedure sets the focus to a specified form:
ActivateForm(FormNo:byte);
Instructs Gold to direct all IO procedures and functions
toward the specified form. The form must fall within the range 1 to
the value specified in the call to CreateForms.
The following code fragment shows how ActivateForm might be
used to configure multiple forms:
begin
CreateForms(4);
ActivateTable(1);
...{work on form 1}
ActivateForm(2);
...{work on form 2}
end;
Creating Dialogs, i.e. Forms in Windows
If you are using forms (in windows) on the desktop, then refer
to the section Launching Forms on the Desktop at the end of this
chapter. The text below refers to forms created outside of the
desktop environment.
By default, the Gold form routines are configured to work full
screen. If you want to display a form in a window you must advise
Gold that the form will be displayed in a window before calling the
field definition functions like AddField, StringField, etc.
Use the procedure SetFormWindow (defined below) to configure
the form to run in a window.
SetFormWindow(X1,Y1,X2,Y2,style:byte);
Instructs Gold to create a form in a window. The first four
parameters define the window coordinates, and the fifth parameter
defines the window style.
Having set the form window, the function FormWinNum will
return the window handle (or number). You can customize (and write
to) a form window, just like any standard Gold window.
The following code fragment is from the demo file DEMIO10.PAS:
procedure SetFields;
{}
var I : Integer;
begin
CreateForms(1);
ActivateForm(1);
{define the form's window}
SetFormWindow(15,4,67,22,1);
WinSetTitle(FormWinNum,' Customer Comments ');
WinSetType(FormWinNum,WMove);
WinSetShowNum(FormWinNum,false);
KwikAddField(1,10,2);
KwikAddField(2,10,14);
KwikAddField(3,35,14);
KwikAddField(4,35,16);
...
end;
Gold will dispose of the window automatically when you call
DisposeFields.
Customizing a Form's Appearance and Behavior
Overriding the Form Navigation Keys
Most of a form's characteristics are controlled by the
individual fields in a form. The keys used to jump from field to
field, however, are set for the entire table.
By default, the keystrokes to shift to the next field up,
down, left and right are the Up cursor, Down cursor, Ctrl-Left
cursor and Ctrl-Right cursor respectively. The de facto standard of
using the Tab and Shift-Tab keys to move to the next and previous
fields are also supported.
The F10 key is assigned to be the finished key, Esc is the
escaped key, and Ctrl-E is the erase field key.
You can customize these keys using one of the following three
procedures:
AssignActionChars(Nxt,Prv,U,D,L,R,Fin,Esc,E: word);
Defines the keys the user will use to navigate the form. These
nine word parameters represent the key codes to move to the next
field, the previous field, the field above the field below, the
field to the left, the field to the right, the key to finish
editing, the key to abort editing and the key to erase the entire
field contents, respectively.
Here is a little tip; if you pass a keycode of zero, the
keystroke assignment for that key will not be changed. This
provides a quick and easy way to change some, but not all, of the
key codes. For example, the following statement will only redefine
the up, down, left and right keys:
AssignActionChars(0,0,278,288,294,275,0,0,0);
AssignFinishChar(W:word);
Provides a quick way to change the finished editing key, which
defaults to F10.
AllowEsc(On:boolean);
Controls whether the user can Esc from the form. Pass TRUE to
enable the Esc key, and FALSE to disable it.
Customizing Colors
There are many different colors that might be used in a form.
In fact there are 37 tint elements that affect forms and they are
defined as follows:
IOEditErase,
IOEditNorm,
IOEditHi,
IOEditOff,
IOButtonNorm,
IOButtonNormHot,
IOButtonHi,
IOButtonHiHot,
IOButtonOff,
IOButtonDef,
IOButtonDefHot,
IOMessage,
IOLabelNorm,
IOLabelNormHot,
IOLabelHi,
IOLabelHiHot,
IOLabelOff,
IOWinBorder1,
IOWinBorder2,
IOWinTitle,
IOWinBody,
IOWinIcons,
IOWinBorderOff,
IOCheckArea,
IOChoiceHi,
IOChoiceHiHot,
IOChoiceNorm,
IOChoiceNormHot,
IOChoiceOff,
IOIcons,
IOIcons2,
IOListHi,
IOListHiHot,
IOListNorm,
IOListNormHot,
IOListOff,
IOListScroll
If you want to change the color of all the forms in your
application, then you should call the GoldSetColor function and
modify the Gold defaults, prior to calling any of the IO procedures
and functions. If you want to change the colors for a specific
form, then call the IOSetColor function which is defined as
follows:
IOSetColor(A:TintElement;C:byte);
Changes the color of a single element of the active form.
Listed below is an extract from the demo file DEMIO6.PAS which
customizes the colors in a form.
procedure CustomizeColors;
{}
begin
IoSetColor(IOEditErase,WhiteonRed);
IoSetColor(IOEditNorm,WhiteOnMagenta);
IoSetColor(IOEditHi,YellowOnGreen);
IoSetColor(IOEditOff,LightgrayOnMagenta);
IoSetColor(IOButtonNorm,WhiteOnBlue);
IoSetColor(IOButtonNormHot,YellowOnBlue);
IoSetColor(IOButtonHi,WhiteOnRed);
IoSetColor(IOButtonHiHot,YellowOnRed);
IoSetColor(IOButtonOff,WhiteOnBlack);
IoSetColor(IOButtonDef,LightgrayOnBlue);
IoSetColor(IOButtonDefHot,LightcyanOnBlue);
IoSetColor(IOMessage,WhiteOnBlack);
...
end; { CustomizeColors }
Understanding Field Rules
The field rules define the characteristics of the field. For
example, whether the field is right justified or left justified.
The rules are stored for each field individually, and the
programmer can change any or all of the rules as desired.
There are five different field rules, defined as follows:
AllowNull - indicates the user is allowed to leave the field
empty.
SuppressZero - the field is displayed as blank in a number
field if the value is zero.
RightJustify - the field grows from right to left rather than
the normal left to right.
EraseDefault - when the user enters the field, the default (or
previous value) is erased if the user presses a normal
alphanumeric key.
JumpIfFull - move to the next field to the right if the field
is filled with data, i.e. if the user has entered
sufficient characters to fill the field.
These five values are defined as constants in GOLDIO.PAS, and
can be summed to combine field rules. For example, a field may be
defined as having rules AllowNull + SuppressZero. If one of the
constants is omitted from a field rule's declaration, then that
rule will not be enforced. In the example just mentioned, for
example, the field would not be right justified.
If you want none of the rules to apply, then specify the
constant NoRules as the only field rule.
To set the default rules for an entire form, call the
following procedure before adding the fields:
SetDefaultRules(Rules:word);
Defines the default rules that will be applied to any fields
subsequently created with AddField.
Miscellaneous Form Defaults
The following procedures and functions are used to set other
default characteristics of a form:
SetMessageXY(X,Y:byte; InWindow: boolean);
Defines the location of where field messages will be
displayed. These messages provide the user with information about
the field with focus. For example, a message might be "Enter an age
in the range 15 to 65". If the form is being displayed in a window,
and the message will be displayed inside the window, set the
InWindow parameter to TRUE, otherwise set it to false.
SetInsertMode(On:boolean);
Controls whether the user is initially in insert or overtype
mode. Pass TRUE to start the form in insert mode.
SetValidation(Val:gValidate);
Gold offers automatic field validation on numeric and date
fields. The validation checks can be performed when the user moves
from one field to the next, or when the user attempts to finish
input (by clicking on the OK button, or pressing F10, for example).
The procedure accepts one of the following two values:
gValidate = (ValidatebyField,ValidateAtEnd);
Placing Fields in a Form
Having defined the form's main properties, you need to place
the fields on the form. At this point Gold doesn't need to know
anything about the field except where it is positioned, and which
fields will be adjacent to it.
AddField(FieldID,DefU,DefD,DefL,DefR,DefX,DefY:byte);
Adds a field to a form. The first parameter identifies the
logical field number. This must be unique, and it is recommended
that the fields are added in ascending numerical order. This same
field identifier is used in subsequent calls to change the field's
properties. The next four parameters identify the field ID of the
fields which will be jumped to when the user attempts to navigate
upward, downward, leftward and rightward, respectively. The final
two parameters identify the (X,Y) coordinate of the first character
of the field.
KwikAddField(FieldID,DefX,DefY:byte);
Performs the same function as AddField but leaves Gold to
assign the field jumps. Upward and leftward will jump to the field
whose ID is one less than the current field. Downward and rightward
will jump to the field whose ID is one greater than the current
field. Don't use this function to add the last field in a form,
since there will not be a field with an ID greater than the current
field -- use KwikAddLastField below.
KwikAddLastField(FieldID,DefX,DefY:byte);
Adds the last field to a form. See KwikAddField above.
One family of fields, invisible fields, cannot be added with
any of the three functions described above. They are not visible
and so do not have (X,Y) coordinates, and the user never lands on
or edits them. These fields are referred to as HotKey fields, and
are added to the form using the following procedure:
AddHotKeyField(FieldID:byte; Key:word; Action:gAction);
Adds a hotkey (non-visible) field to a form. The first
parameter identifies the field ID, the second parameter the word
value of the hotkey, and the third value identifies the action code
invoked when the user presses the specified key. Listed below is
the enumerated type gAction defined in GOLDIO.PAS.
gAction = (None,NextField,PrevField,NextForm,PrevForm,
Refresh,Enter,Help,Stop1,Stop2,...Stop97,Stop98,Stop99,
Finished,Cancel1,Cancel2,Cancel3,Cancel4,Cancel5,
Cancel6,Cancel7,Cancel8,Cancel9,Escaped);
The following three functions apply to all visible fields, and
can be used to set field messages, labels and hotkeys:
SetMessage(FieldID,X,Y:integer; Str : string);
Defines the message which will be displayed when the field has
focus. The message location is controlled by using the procedure
SetMessageXY, discussed earlier.
SetLabel(FieldID,X,Y:integer; Str : string);
Identifies a label that will be printed when the form is
displayed. In most forms each field will have a label. Although you
can specify an explicit XY location, to literally put the label
anywhere on the form, in most situations, use the constant
LabelLeft or LabelTop to instruct Gold to automatically position
the label relative to the field.
SetHK(FieldID:byte; Hotkey: word);
Defines the keystroke which a user can press to automatically
jump to the field. The keystroke should normally correspond to a
highlighted letter in the field label. For example, if the letter A
is highlighted, the hotkey would be assigned as AltA, i.e. 286.
Individual Field Rules
SetDefaultRules defines the default field input rules for a
form. Individual field rules can be specified using the procedure
FieldRules. In addition to identifying the field rules as discussed
earlier, the FieldRules procedure can control the specific
characters which the user will be allowed to input. These character
rules go beyond the simple field masks such as @, *, ! and #.
For example, the only valid characters for a field that
identifies a warehouse part number may be "A B C 1 2 3 4 5 6".
Alternatively, a field that is a filename may allow any
alphanumeric character except the * and ? characters. Both of these
conditions can be accommodated using the FieldRules procedure
defined as follows:
FieldRules(FieldID:byte;Rules:word;AChar:IOcharset;
DChar:IOcharset);
Defines the field rules for a specific field. The second
parameter identifies the field rules and can be any combination of
AllowNull, SuppressZero, RightJustify, EraseDefault and JumpIfFull.
The third parameter is of type set of char and identifies the
allowable characters. These characters should be defined within
square parentheses, e.g. ['A'..'C','1'..'6']. The constant [NoChar]
can be used to indicate that no special character restrictions
apply. The fourth parameter is also of type set of char and
identifies disallowed characters, e.g. ['*','?']. If no characters
are to be disallowed, use the constant [NoChar].
Controlling Field States
The following two procedures are used to set and get the state
of a field, i.e. whether the field is enabled, disabled or hidden.
As you might imagine, hidden fields are not visible and cannot have
focus. Disabled fields, however, are visible but are displayed in a
grayed style, and cannot have focus. The gActiveState is an
enumerated type defined in GOLDIO.PAS as follows:
gActiveState = (FldOff, FldOn, FldHidden);
FieldSetState(FieldID:byte; State:gActiveState);
Sets the state of the field to enabled, disabled or hidden.
FieldGetState(FieldID:byte):gActiveState;
Returns the state of the specified field.
Defining Each Field's Properties
Typical fields allow the user to enter text or numbers, but
there are also radio buttons, check boxes, push buttons, etc.
Having placed the fields on the form (with AddField or
KwikAddField), you must now define each field's properties. For
example, field 1 might accept text input, and field 2 might be a
push button, etc. Gold includes 27 different field types for your
programming pleasure and they are organized into the following
categories:
String Fields
Number Fields
Date Fields
Push Buttons
Radio Buttons and Check Boxes
List Fields
Multi-Line Edit Fields
Hot Spots
Understanding Field Formats
All the standard fields (i.e. string, number and date fields)
accept a string variable called DefFormat. This string uses Gold's
special mask characters to define the field's width and optionally
any mask characters, i.e. characters which are displayed in the
input field, but are non-editable, and the cursor automatically
jumps past these mask characters.
There are four pre-defined format characters in Gold:
# Allow 0..9, '.','-', and 'e' for scientific
@ Only letters of the alphabet and punctuation
* Any character the user can find!
! Like @, but all characters are converted to uppercase
Any other characters identified in the field's format string
are treated as fixed and for display only. For example, if the
input field is a telephone number, then the format might be defined
as '(###) ###-####'. The user will only be allowed to input
numbers, and after typing three numbers, the cursor will jump three
positions to the right (to the next # character).
As you may recall, every field is associated with an
individual variable. Note that this variable is not updated with
the formatting characters. For example, if the input field on the
screen displayed "(409) 737-5472" the variable would be updated
with the string "4097375472. This saves space if the variables are
stored in a database.
The GoldSTR unit contains the function PicFormat which is
passed a string and a picture, and which returns the combined
formatted string.
String Fields
The string fields form the backbone of user input forms as
they are used to gather string data from the user, e.g. names,
addresses, part numbers, etc. Two different string field types are
provided for your coding pleasure: a StringField is of fixed width
and supports the formatting characters; the ScrollField allows the
user to input a string that is wider than the displayed field
width, but does not support special formatting characters. The
fields are defined as follows:
StringField(FieldID:byte;var Strvar:String;DefFormat:string);
Basic input field allowing the user to input a string. The
width of the field is defined by the DefFormat parameter. For
example, a twelve character name field might be defined with a
DefFormat of '************' or a shortcut would be to use the
Replicate function, i.e. Replicate(12,'*');
ScrollField(FieldID:byte; var Strvar:string;FieldL,MaxL:byte);
Accepts string input and provides scrolling features, allowing
the user to input more characters than the visible field width. Use
this field type when you have limited space on the form, or when
you want to allow the user to enter a large number of characters,
and don't want a humongo field displayed. The last two parameters
define the visible field width and the maximum number of characters
which the user may input, respectively.
Number Fields
As you might imagine, number fields should be used when you
want the user to input a number. A variety of different field types
are provided. The ByteField, WordField, IntegerField, LongIntField
procedures all accept whole numbers, and are defined as follows:
ByteField(FieldID:byte;var Bytevar:Byte;DefFormat:string;
Min,Max:byte);
WordField(FieldID:byte;var Wordvar:Word;DefFormat:string;
Min,Max:word);
IntegerField(FieldID:byte;var Integervar:Integer;DefFormat:string;
Min,Max:Integer);
LongIntField(FieldID:byte;var LongIntvar:LongInt;DefFormat:string;
Min,Max : LongInt);
The DefFormat passed to these four fields defines the field
width and although mask characters are supported, you will
typically use the # character to indicate that numbers should be
input.
Each of the four field types accepts Min and Max numbers
indicating the valid number range that you want to impose on the
user. For example, an age field might be set to accept ranges from
age 12 to age 65. If one of the Min or Max values is non zero, Gold
will automatically impose the range restrictions. The brighter
among you may have deduced that the Min and Max should both be set
to zero when you want to accept any number supported by the
variable type.
Some modern applications provide spinner icons to allow the
user to quickly scroll through a number series. Gold provides the
SpinLongField function for this purpose (defined below). If you
want the user to input a whole number within a specific range, and
offer spinners, then use SpinLongField, but remember that the
variable must be of type LongInt.
SpinLongField(FieldID:byte; var LongIntvar:LongInt;Width:byte;
Min,Max,Increment : LongInt);
Gold provides two field types for the entry of real numbers.
Use FixedRealField to get input when there are a fixed number of
decimal places required. For example, a FixedRealField would be
ideal for inputting dollar values where two decimal places would be
used for the cents. If, on the other hand, the number of decimal
places is at the discretion of the user, use a RealField, because
the user can enter the decimal point (or whatever obscure character
your country may utilize for this purpose!) where appropriate.
RealField(FieldID:byte;var Realvar:extended;DefFormat:string;
Min,Max:extended);
FixedRealField(FieldID:byte; var Realvar:Extended;Whole,DP:byte;
Min,Max:extended);
As the name suggests, the SpinRealField (defined below)
provides real field input with spinners. The last parameter defines
how much the input number will be incremented or decremented when
the spinner is clicked.
SpinRealField(FieldID:byte; var Realvar:extended;Whole,DP:byte;
Min,Max,Delta:extended);
Date Fields
While Gold won't make you more attractive to the opposite sex,
it will allow you to get dates from your users! Gold supports eight
different date formats, which are identified using the gDate
enumerated type defined in GoldDate as follows (see Chapter 19):
gDate = (MMDDYY,MMDDYYYY,MMYY,MMYYYY,DDMMYY,DDMMYYYY,
YYMMDD,YYYYMMDD);
You can use one of four different field types to garner date
information from the user. The traditional DateField allows the
user to input a date using the keyboard, the traditional way. The
SpinDateField adds spinners to the standard date field, allowing
the user to easily scroll backwards and forwards through dates.
If you want the user to optionally select the date from a
pop-up calendar (and let's face it, who wouldn't?!), use the
DropDateField. This field sports an arrow to the right of the
field. When the user clicks on the arrow, a calendar is displayed.
If you want spinners and a pop-up calendar, you guessed it, use the
SpinDropDateField. These four fields are defined as follows:
DateField(FieldID:byte;var Datevar:Dates;DateFormat:gDate;
DefFormat:string;Min,Max : Dates);
SpinDateField(FieldID:byte; var Datevar:Dates;
DateFormat:gDate;DefFormat:string; Min,Max : Dates);
DropDateField(FieldID:byte; var Datevar:Dates;
DateFormat:gDate;DefFormat:string; Min,Max : Dates);
SpinDropDateField(FieldID:byte;var Datevar:Dates; DateFormat:gDate;
DefFormat:string; Min,Max : Dates);
Each date field takes a variable of type dates. As with the
number fields, the Min and Max variables define the earliest and
latest dates which you will allow the user to input. (These dates
are Julian; refer to Chapter 19 to find out more about Julian
dates.) Specify a Min and Max of zero if you want to accept any
valid date. Note that Gold will always ensure that the date is
valid, e.g. November 31st, 1956 would not be accepted.
Each of the four date fields accepts a DefFormat string. This
parameter is primarily offered for backward compatibility and to
provide support for other date formats not intrinsically supported
by Gold. Typically, pass a null string as the DefFormat and Gold
will assign the appropriate picture characters based on the
DateFormat specified.
Push Buttons
Push button fields don't accept direct user input, i.e. you
can't enter data into a "button". Button fields, therefore, are not
linked to a variable. Each button added to a form has an action
code of type gAction. When the user presses the button, the
button's action code is processed. This results in the form editing
session finishing, and the action code returned by the form is the
action code assigned to the button. You may recall that gAction is
defined in the GoldIO unit as follows:
gAction = (None,NextField,PrevField,NextForm,PrevForm,
Refresh,Enter,Help,Stop1,Stop2,...Stop97,Stop98,Stop99,
Finished,Cancel1,Cancel2,Cancel3,Cancel4,Cancel5,
Cancel6,Cancel7,Cancel8,Cancel9,Escaped);
Buttons can be added to a form using ButtonField or
ButtonDefaultField, which are defined as follows:
ButtonField(FieldID:byte; Face:string; Action:gAction);
ButtonDefaultField(FieldID:byte; Face:string; Action:gAction);
The second parameter identifies the text that will be
displayed on the button, and the third parameter is the gAction
code that will be returned by the form when the button is pressed.
Use the ButtonDefaultField to identify a button which will be
"pressed" when the user presses the Enter key -- there should only
be one default button field on a form.
The ButtonChangeSettings procedure (defined below) can be used
to dynamically change the face and action code of a button. This
might be used, for example, to change an "Edit" button to a "Save"
button when the button is pressed.
ButtonChangeSettings(FieldID:byte; Face:string; Action:gAction);
The following code fragments are from the demo file
DEMIO13.PAS which uses buttons to help the user tag and untag
selections in another field.
procedure SetFields;
{}
begin
.....
KwikAddField(1, 1,2);
KwikAddField(2, 6,19);
KwikAddField(3, 18,19);
KwikAddField(4, 33,19);
KwikAddField(5, 49,19);
KwikAddLastField(6, 62,19);
AssignHindHook(HindHook);
{The List}
FillTheList;
ConfiguretheListFormat;
ListAssignSLL(ListFormat,SourceList);
WrapListField(1,75,1,14,ListFormat); {Buttons}
ButtonField(2,' ~T~ag ',Stop1);
ButtonField(3,' Tag ~A~ll ',Stop2);
ButtonField(4,' ~U~ntag all ',Stop3);
ButtonDefaultField(5,' ~O~K ',finished);
ButtonField(6,' ~C~ancel ',escaped);
SetHK(2,276); {Alt+T}
SetHK(3,286); {Alt+A}
SetHK(4,278); {Alt+U}
SetHK(5,280); {Alt+O}
SetHK(6,302); {Alt+C}
end; {SetFields}
repeat
DisplayAllFields;
EditAction := EditForm(1);
case EditAction of
Stop1: begin
ItemIsTagged := SLLGetTagState
(ListFormat.ActiveNode);
SLLSetBit(SLLNodePtr(ListFormat.ActiveNode),
TagBit, not ItemIsTagged);
SLLSetBit(SLLNodePtr(ListFormat.ActiveNode),
ColBit, not ItemIsTagged);
end;
Stop2: begin
SetTagAll(ListFormat,true);
end;
Stop3: begin
SetTagAll(ListFormat,false);
end;
end; {case}
CheckTagButton;
until EditAction in [Finished,Escaped];
Note that the form is displayed in a repeat loop -- every time
a button is pressed the EditForm function (discussed later) returns
the value of the button pressed by the user. Although the user
isn't aware of it, the form is being closed and opened many times
during the edit session.
Radio Buttons and Check Boxes
Two common elements of contemporary input forms are check
boxes and radio buttons. Both these objects provide ways of
choosing items from collections of options.
A check box field provides a list of options with each item
having its own check box displayed to the left of the item, e.g.
[X] Toast. Any number of items in the list can be selected. An
item's selection status is toggled by hitting the space bar (even
with your forehead) or by clicking the mouse cursor on it. Selected
items have an X in the adjacent check box.
Radio buttons are similar to check boxes, except that only one
item can be selected at any one time. When an item is selected, the
previously selected item is deselected. The selected item has a dot
in the "button" next to it, e.g. (.) Ham Sandwich.
Radio Buttons
To define a radio button field, you must define the field
using RadioField, and then call RadioAddItem once for each option
in the field. These procedures are defined as follows:
RadioField(FieldID,width,depth:byte; var SelectedItem:byte);
The Width and Depth fields define the overall dimensions of
the radio button field, in characters and lines, respectively --
this is analogous to defining the groupbox in Windows (just a note
for you Delphi jocks). The SelectedItem variable indicates which
radio button is selected by the user. Be sure to assign a suitable
value to this variable before running the form.
RadioAddItem(FieldID,ItemX,ItemY:integer; Str,Msg:string;
ItemHK:word);
Defines a specific item in a Radio field. Note the ItemX and
ItemY parameters define the position of the item relative to the
topleft of the Radio field (not the top left of the form).
CheckFields
To define a check button field, you must define the field
using CheckField, and then call CheckAddItem once for each option
in the field. These procedures are defined as follows:
CheckField(FieldID,width,depth:byte);
The Width and Depth fields define the overall demensions of
the radio button field, in characters and lines, respectively.
CheckAddItem(FieldID,ItemX,ItemY:integer; Str,Msg:string;
ItemHK:word;var gResult:boolean);
Defines a specific item in a Check field. Note the ItemX and
ItemY parameters define the position of the item relative to the
topleft of the Radio field (not the top left of the form). The
gResult variable is set to true when the item is checked, and false
when it is not checked.
Disabling Radio or Check Button Items
If you want one element of a radio button or check box field
to be disabled, use CheckRadioSetActive which is defined as
follows:
CheckRadioSetActive(FieldID,ItemNum:integer;IsActive:boolean);
Sets the enabled state of an individual radio button or check
box item. The ItemNum parameter represents the individual element
whose state will be changed -- the first added item has a value of
1, the second a value of 2, and so on.
The following code fragment is from the demo file DEMIO6.PAS,
and show how to construct radio button and checkbox fields:
{Radio Buttons}
RadioField(9, 25,4,Cust.Radio);
RadioAddItem(9, 1,1, 'Corporation','',0);
RadioAddItem(9, 1,2, 'S Corporation','',0);
RadioAddItem(9, 1,3, 'DBA','',0);
RadioAddItem(9, 1,4, 'Individual','',0);
SetLabel(9,LabelTop,LabelTop,'Business ~T~ype');
SetHK(9,276);
{Check Boxes}
CheckField(10, 25,4);
CheckAddItem(10, 1,1, 'Small Business','',0,
Cust.Check1);
CheckAddItem(10, 1,2, 'Minority Owned','',0,
Cust.Check2);
CheckAddItem(10, 1,3, 'English Owned','',0,
Cust.Check3);
CheckAddItem(10, 1,4, 'Woman Owned','',0,Cust.Check4);
CheckRadioSetActive(10,3,false);
SetLabel(10,LabelTop,LabelTop,'~B~usiness Categeory');
SetHK(10,304);
List Fields
Gold supports basic list fields that can be created and
defined in a couple of fields, as well as gut-busting lists which
have all the power of the list engine defined in the GoldLIST unit.
Basic List Fields
Creating a list field is a two step process. First use
ListField (described below) to define the overall dimensions of the
list.
ListField(FieldID,width,depth:byte; var SelectedItem:integer);
Identfies the width and depth of a list field. The
SelectedItem variable will be updated with the one-based node
number of the selected item.
Individual items in the list can be added using the
ListAddItem function, which is defined as follows:
ListAddItem(FieldID:integer; Str:string);
Adds the specified string to the list. That's all there is to it.
If you want to add a set of fields in one fell swoop, use the
ListKwikAddItem as follows:
ListKwikAddItem(FieldID:integer; Str:string);
Adds a series of items to a list field. All the items are
passed in a single string, and the items are separated with the
split bar character "|".
The following code fragment shows KwikListAddItem in use:
SpinDropListField(1,25,PrintInfo.TypeFaceID);
ListKwikAddItem(1,'Adelaide|Arial MT|Bookman|Courier'
ListKwikAddItem(1,'Dom Casual |Freestyle|Script');
ListKwikAddItem(1,'Helvetica|Juniper|Kidnap|Lithos');
ListKwikAddItem(1,'Light|Times Roman|Zap Dingbobs');
In addition to the standard rectangular list box, Gold can
display lists using formats similar to the Window's ComboBox. Lists
can be displayed in a drop-down box, as a spinner field, or both
features can be combined into a single spin drop list field. These
different formats can be implemented by using one of the following
three procedures in place of the standard ListField procedure:
DropListField(FieldID,width:byte; var SelectedItem:integer);
SpinListField(FieldID,width:byte; var SelectedItem:integer);
SpinDropListField(FieldID,width:byte;
var SelectedItem:integer);
Use the EditDropListField (defined below) if you want the user
to be able to type text into the field or optionally select the
text from a drop-down list box.
EditDropListField(FieldID:byte; var Strvar:string;
FieldL,MaxL:byte);
To change the contents of a list field "on the fly", you
should call ListRebuild (defined below) and pass the list contents
as a single string using the KwikAddField format.
ListRebuild(FieldID:integer; Str:string);
If you want to get the literal string of the list items
selected by the user, you can use the ListGetString function which
is defined as follows:
ListGetString(FieldID:integer; EntryNo:integer): string;
Power List Fields
To display lists in forms which have advanced features such as
tagging, wrapping, etc., you'll need to create a string linked
list, and then use one of the power list fields. The power list
fields use the list engine defined in the GoldLIST unit. We suggest
you also read Chapters 14 and 15 to gain a thorough understanding
of Gold's power field support.
To display a list defined in an StrLL (a simple string linked
list), first create the field using ListField, SpinDropListField
(or any of the other variations) and then use ListAssignStrLL to
define the list contents.
ListAssignStrLL(FieldID:integer; var SL:StringLL);
To change the list contents "on the fly", use the following
procedure in preference to ListRebuild:
ListUpdateStrLL(FieldID:integer; var SL:StringLL);
The most powerful list fields provide all the functionality of
Gold's list windows as a field in a form. Before adding the list
field to the form, using one of the three procedures listed below,
first create a linked list and a ListCfg variable following the
same principles as outlined in Chapter 14.
WrapListField(FieldID,Colwidth,ColCount,RowCount:byte;
var ListDetails: ListCfg);
GridListField(FieldID,width,depth:byte;var ListDetails: ListCfg);
BrowseField(FieldID,width,depth:byte; var ListDetails: ListCfg);
Use the following two functions to ascertain the user's
selection from one of these power fields, along with the last key
that was pressed when the list had focus in the form:
ListGetActivePick(FieldID:integer): integer;
ListLastKey(FieldID:byte):word;
Multi-Line Edit Fields
Chapter 15 described how to use Gold's editor to add editing
capabilities to an application. The same editor engine can be
accessed from a form by using a memo field. It goes without saying
that you need to read Chapter 15 to get the most out of the memo
field.
To add a memo field to a form, create a memo data source, such
as a linked list, create and set a MemoCfg variable and then call
MemoField, which is defined as follows:
MemoField(FieldID,width,depth:byte;var Memo:MemoCfg);
Hot Spots
When is a field not a field? When it's a hotspot!
A hot spot field is really an invisible button. When you click
on a hot spot, Gold will end the form edit session and return the
gAction code assigned to the hotspot. Define a hot spot field using
the following procedure:
HotspotField(FieldID:byte; Width,Depth: byte; Action:gAction);
Although the field seems to have little value, you can use it
in conjunction with plain text to create custom buttons. For
example, you might draw four different boxes on the form and write
some text within each box. These text boxes are not active parts of
the form, but by adding hotspots which overlay the precise area of
the box, you can respond to user clicks in the rectangle.
Running The Form
Using EditForm
Once the form has been fully defined, use the EditForm
function to pass control to the user and let them get on with the
input. EditForm is defined as follows:
EditForm(StartField:byte):gAction;
Displays the active form and allows the user to edit data in
the fields. The single passed parameter indicates the FieldID of
the field which will be initially focused when the edit session
begins. The function returns a value of type gAction indicating how
the edit session was terminated. This may be Finished, Escaped, or
any of the codes assigned to buttons on the form.
You should check the value returned by EditForm to determine
whether the user was aborting, or the user wanted the edits
committed.
The following code fragment illustrates this technique:
if EditForm(1) = Finished then
SaveTheEdits
else
IgnoreTheEdits;
The function FieldWithFocus returns the FieldID of the field
with focus. If you are using EditForm in a loop, use FieldWithFocus
to determine which field to give focus on the next iteration. The
following code fragment (extracted from DEMDB1.PAS) demonstrates
this technique:
ActiveField := 13;
repeat
RecNum := X;
if (DbGetNumRecs > 0) and (X > 0) then
DatabaseToScreen(X);
DisplayAllFields;
LastAction := EditForm(ActiveField);
ActiveField := FieldWithFocus;
case LastAction of
Stop1: begin
X := NdxGotoFirst;
ActiveField := 13; { next }
end;
Stop2: begin
X := NdxGotoPrev;
if X = 0 then
begin
X := NdxGotoLast;
PromptOK(' Top of file ','^Loop...')
end;
end;
Stop3: begin
X := NdxGotoNext;
if X = 0 then
begin
X := NdxGotoFirst;
PromptOK(' End of file ','^Loo...');
end;
end;
Stop4: begin
X := NdxGotoLast;
ActiveField := 12; { prev }
end;
end; { case }
until LastAction = Escaped;
DisposeFields;
DisposeForms;
Old TTTimers might be familiar with the TTT5 procedure
ProcessInput. This procedure is still supported. The function
FormExitAction can be called to determine gAction value used to end
the edit session.
Displaying The Form
EditForm will automatically display the fields and labels in
the form before commencing with the edit session. You can, however,
display the form without passing control to the user by call the
DisplayForm procedure, which is defined as follows:
DisplayForm;
Instructs Gold to display all the fields and labels associated
with the active form.
Behind the scenes, DisplayForm calls the two procedures
DisplayAllLabels and DisplayAllFields. You can call these
procedures individually if you prefer. For example, within an edit
loop, you might want to call DisplayAllfields with each iteration
of the loop because the data in the fields change each time around
(see the code fragment on the previous page), but you might only
call DisplayAllLabels once before starting the loop.
Closing and Disposing of a Form
Behind the scenes, forms are created on the heap in a custom
form of linked list. You must dispose of the memory used by a form
when the form is no longer required.
The primary way to dispose of a form is to call the procedure
DisposeFields. This procedure will remove all the field data from
memory and effectively erase all information associated with the
active table.
If you have called DisposeFields for a table, you can (at a
later time) redefine the table with a new set of fields.
Using DisposeForms
Gold maintains a list of information about each form, e.g.
whether the form has any fields defined, is the form in a window,
etc. When you have no further need for any of the forms, you can
call DisposeForms to free every last drop of memory used by the
forms.
Using Hooks in Forms
Brilliant though Gold is, there will be occasions when the
form routines provided do not meet your exact requirements. There
are six different custom hooks which will allow you to precisely
customize a form's behavior to meet your specific needs.
The hooks provide you with information about the current state
of the form and the task being performed, and allow you to
influence the execution of the task. For example, the user may be
attempting to leave field 2 (perhaps by pressing the tab key or
clicking with the mouse on another field). You could use a leave
field hook to trap for the field change event, and only permit the
user to leave field 2 when the data in the field is satisfactory.
Gold provides the following form hooks:
Character Hook
Called every time the user presses a character.
Hind Hook
Called after a every character has been processed.
Insert Hook
Called when the user presses the insert key.
Leave Field Hook
Called every time the user tries to leave one field and
move to another.
Enter Fields Hook
Called every time the user tries to enter a field, i.e. gives
a field focus.
Finished Hook
Called when the user tries to close the form.
Intercepting Every Character
Two different hooks allow you to trap each character as it is
processed. The character hook gives you a chance to review a
character before it is processed by Gold, and the Hind hook is
called after each character has been processed.
Character Hook
For a procedure to be eligible as a form character hook, it
must adhere to the following rules:
The function must be declared as a far function at the root
level. Refer to section Understanding Hooks in Chapter 3 for
further information.
The function must be declared with three passed parameters.
The first parameter is a variable word parameter which
indicates the key which is about to be processed. The second
parameter is a variable byte parameter, indicating the ID of
the field with focus. The third parameter is also a variable
of type byte which can be used to return a Refresh code,
instructing Gold on how to proceed.
The following procedure declaration follows these rules:
{$F+}
procedure KeyPressCheck(var C : word;var CF:byte;
var Refresh:byte);
begin
If C = 315 then
begin
Help;
C := 0;
end;
end;
{$F-}
The following procedure is then called to instruct Gold to
call your procedure every time a character is about to be
processed:
AssignCharHook(Proc:CharHookProc);
Here are a few tips about writing character hooks:
If you want to trap for a special character (such as Alt-H),
perform some custom task, and then stop Gold from trying to process
this character, assign a value of zero to the character and Gold
will ignore it.
The Refresh parameter is used to instruct Gold to perform some
special screen refreshing task. The constants (listed below) are
declared in the GoldIO unit and should be used for this purpose.
Assign one of these values to the third passed parameter if you
want Gold to perform some special field refreshing operation, or if
you want to end the edit session.
RefreshNone = 0;
RefreshCurrent = 1;
RefreshAll = 2;
RefreshOthers = 3;
EndInput = 99;
Hind Hook
Like the character hook, the hind hook is called every time a
key is pressed by the user. The hind hook, however, is called after
the character has been processed by Gold. One use for a hind hook
provides is to write some text to the screen based on the data
entered into other fields; the sub-totals on an invoice form, for
example. A hind hook is often used to change the enabled/disabled
state of a field based on the value of other fields, e.g. if a font
check box is not checked, then the hind hook might disable or hide
the font list box.
For a procedure to be eligible as a form character hook, it
must adhere to the following rules:
The function must be declared as a far function at the root
level. Refer to section Understanding Hooks in Chapter 3 for
further information.
The function must be declared with two passed parameters. The
first parameter is a byte parameter indicating the ID of the
field with focus. The second parameter is a variable of type
byte which can be used to return a Refresh code, instructing
Gold on how to proceed.
The following procedure declaration follows these rules:
{$F+}
procedure MyHindHook(CurrentField:byte;var Refresh:byte);
{}
begin
Refresh := RefreshNone;
if CurrentField = 9 then {radio button}
begin
if (Cust.Radio = 4)
and (FieldGetState(10) = FldOn) then
begin
FieldSetState(10,FldOff);
Refresh := RefreshOthers;
end
else if (Cust.Radio <> 4)
and not (FieldGetState(10) = FldOn) then
begin
FieldSetState(10,FldOn);
Refresh := RefreshOthers;
end;
end;
end; {MyHindHook}
{$F-}
The following procedure is then called to instruct Gold to
call your procedure every time a character has been processed:
AssignHindHook(Proc:HindHookProc);
The demo program DEMIO6.PAS shows a hind hook being used to
enable and disable fields.
Insert Hook
The insert hook is called every time the user presses the
insert key. By default, Gold sets the cursor to a full block in
overtype mode, and an underscore in insert mode. The hook allows
the programmer to introduce some other method of indicating the
insert status, e.g. writing "insert" or "replace" somewhere on the
screen.
For a procedure to be eligible as a insert hook, it must
adhere to the following rules:
The function must be declared as a far function at the root
level. Refer to section Understanding Hooks in Chapter 3 for
further information.
The function must be declared with one boolean parameter. This
parameter indicates the new state of the insert key.
The following procedure declaration adheres to these rules:
{$F+}
procedure MyInsHook(Insert:boolean);
begin
...
end;
{$F-}
The following procedure is then called to instruct Gold to
call your procedure every time the insert key is pressed:
AssignInsHook(Proc:InsProc);
Field Changing Hooks
Gold provides leave field and enter field hooks. These
functions are called when the user tries to leave a field, and when
the user tries to enter a field. The leave field hook provides a
way to ensure that the contents of a field are valid before moving
to another field. The enter field hook can be used to take some
action before entering a field, e.g. display a warning message, or
move the user to another field if a specific condition is not met.
You declare leave field and enter field hooks in the same way,
i.e. they accept the same arguments. For a procedure to be eligible
as a leave or enter field hook, it must adhere to the following
rules:
The procedure must be declared as a far function at the root
level. Refer to section Understanding Hooks in Chapter 3 for
further information.
The procedure must be declared with two variable byte
parameters. The first parameter indicates the ID of the field
which the user is about to leave or enter. The second
parameter is a refresh code which may be updated to instruct
Gold to either terminate the edit session or refresh one or
more fields. You can use one of the following constants to
indicate the appropriate refresh action:
RefreshNone = 0;
RefreshCurrent = 1;
RefreshAll = 2;
RefreshOthers = 3;
EndInput = 99;
One of the following two procedures can then be used to
instruct Gold to call your procedure every time the user tries to
leave or enter a field, respectively:
AssignLeaveFieldHook(Proc:MoveFieldProc);
AssignEnterFieldHook(Proc:MoveFieldProc);
The Leave Field Hook
As mentioned earlier, leave field hooks are often used to add
some custom field's validation. For example, you might want to
check that a filename field contains a valid filename. When you
find that a field input is not valid, you can force the user to
stay in the same field (i.e. stop changing focus) by setting the
Field ID parameter to a value of zero.
Listed below is an example of a leave field hook, which
displays an error message and keeps the user in the same field when
the field contains an odd number. (OK, it's a contrived example,
but you get the idea!)
{$F+}
procedure MyLeaveHook(var CurrentField:byte;var Refresh:byte);
{}
begin
if (CurrentField = 3)
and odd(DressSize) then
begin
PromptOK(' Error ','The dress size must be an
even number,e.g. 4,6,8...');
CurrentField := 0;
end;
end;
{$F-}
The Enter Field Hook
An enter field hook is called every time the user changes
focus to a new field. In fact, the hook is called just before the
focus is changed, and so the hook can redirect the focus to a
different field. If you want to direct the focus to a different
field, just assign a new value to the Field ID parameter.
Traditionally, enter field hooks were used to temporarily
disable fields, i.e. to prevent a user from giving a field focus.
Gold now offers the FieldSetState function as a quick and easy way
to disable the field, so the primary need for an enter field hook
has disappeared.
Gold can display a context sensitive message when the user
moves from field to field, but only a single line of text can be
displayed. A good application for an enter field hook is to display
custom multi-line help giving the user instructions relevant to the
field with focus. The following code fragment shows how this could
be done:
{$F+}
procedure MyEnterHook(var CurrentField:byte;var Refresh:byte);
{}
begin
case CurrentField of
1: begin
WriteAT(.....
WriteAT(.....
WriteAT(.....
end;
3: begin
WriteAT(.....
WriteAT(.....
WriteAT(.....
end;
...
end; {case}
end;
{$F-}
The Finished Hook
The leave field hook provides a way to implement custom
validation on a field by field basis. The finish hook, however,
provides a way to implement complete validation when the user tries
to end the edit session (other than by escaping).
The finish hook is called after all Gold's internal validation
routines have been performed. It provides the user with one last
chance to check all the edits before the form is closed.
For a function to be eligible as a finished hook, it must
adhere to the following rules:
The function must be declared as a far function at the root
level. Refer to section Understanding Hooks in Chapter 3 for
further information.
The function must be declared no passed parameters, and must
return a byte value.
The hook function returns the ID of the field which failed
validation. If the function returns a zero, Gold will close the
form. If the function returns a non-zero value, the edit session
will continue and focus will shift to the Field whose ID matches
the value returned by the hook function.
Having declared the hook function, instruct Gold to call your
procedure every time the user tries to close the form by calling
the following procedure:
AssignFinishedHook(Proc:FinishedProc);
The following example shows a finished hook which makes sure
that the first field is not null if the second field (a check box
field) is checked.
{$F+}
function CheckNullState:byte;
{}
begin
if (PrinterHeader = true)
and (Heading = '') then
begin
PromptOK(' Error ','Heading Field cannot be empty
when print is checked');
CheckNullState := 1
end
else
CheckNullState := 0;
end;
{$F+}
Launching Forms on the Desktop
All forms displayed on the desktop must be displayed in a
window. However, in place of the procedure SetFormWindow (discussed
earlier), a desktop form must be initialized with the
LaunchFormInit function which is described below:
LaunchFormInit(X1,Y1,X2,Y2,style:byte;
CloseProc:FormCloseProc):byte;
Initializes a form for use on the desktop. The first five
parameters define the windows coordinates and style, respectively.
The last parameter is the name of a function which will be called
when the window containing the form is about to be removed from the
desktop. LaunchFormInit returns the handle (or number) of the
window which will contain the form.
Once the form is initialized, the standard IO routines can be
called in the normal manner, e.g. KwikAddField, DateField, etc.
When the form has been fully defined, you launch the form
(i.e. make it visible on the desktop) by calling the LaunchForm
procedure which is described as follows:
LaunchForm(StartField:byte);
Displays the form window on the desktop. The single passed
parameter identifies the FieldID of the field which will have focus
when the form is first displayed.
Understanding the FormCloseProc
By design, a form launched on the desktop is non-modal, i.e.
the user can change focus from a form to any other window on the
desktop. The user may close the form at any time using a variety of
methods. The user might click on the OK button on the form, click
on the close icon, or select Close Window from the desktop menu.
Since the desktop is event driven, you have to "wait" for the form
to be closed before using the data entered into the form, calling
DisposeFields and performing all the other necessary house-keeping
duties.
When you prepare a form for use on the desktop (using
LaunchFormInit), you must specify the name of a function declared
to be of type FormCloseProc, which is declared as follows:
FormCloseProc = function(FormID: byte):boolean;
This function will be called by Gold when the user attempts to
close the form. This is your chance to do the housekeeping duties
which you would normally perform after a call to EditForm (in a
non-desktop application). The function returns a boolean to
indicate to Gold whether the form may be closed. If a FALSE value
is returned, the form window will not be closed.
For a function to be eligible as a form close function it must
adhere to the following rules:
The function must be declared as a far function at the root
level. Refer to section Understanding Hooks in Chapter 3 for
further information.
The function must be declared with a single passed parameter
of type byte and return a boolean indicating whether the form
can be closed. The passed parameter indicates the window
handle (or number) of the window that is being closed.
The following function declaration (extracted from
DEMDESK4.PAS) follows these rules:
{$F+}
function FormShutDown(Form:byte):boolean;
{}
begin
BarSetActive(MainMenu,200,true);
DeskRefreshBgnd;
DisposeFields;
DisposeForms;
PrintInfo.TypeSize := Longvar;
FormShutDown := true;
end; { FormShutDown }
{$F-}
Review the demo program DEMDESK4.PAS for a complete example of
using forms in a desktop application.
var
Name,Tel:string;
Age:byte;
Sex: string[1];
Result: gAction;
procedure SetScreen;
{}
begin
...
end; { SetScreen }
procedure SetVars;
begin
Name := '';
Tel := '';
Age := 0;
Sex := '';
end; { SetVars }
procedure SetFields;
begin
{create the 5 fields}
KwikAddField(1, 25,9); {Field 1, column 25, line 9}
KwikAddField(2, 25,11); {Field 2, column 25, line 11}
KwikAddField(3, 25,13); {Field 3, column 25, line 13}
KwikAddField(4, 25,15); {Field 4, column 25, line 15}
KwikAddField(5, 26,18); {Field 5, column 25, line 18}
KwikAddLastField(6, 43,18); {Field 6, column 43, line 18}
{Field 1}
StringField(1, Name, '******************************');
SetLabel(1, LabelLeft,LabelLeft,'Full name:');
{Field 2}
StringField(2, Tel, '(###) ###-####');
SetLabel(2, LabelLeft,LabelLeft,'Tel:');
{Field 3}
ByteField(3, Age, '##', 13,65);
SetLabel(3, LabelLeft,LabelLeft,'Patients age:');
{Field 4}
StringField(4, Sex, '!');
SetLabel(4, LabelLeft,LabelLeft,'Sex:');
{Fields 4 & 5: buttons}
ButtonField(5,' OK ',finished);
ButtonField(6,' Cancel ',escaped);
end; { SetFields }
begin
SetScreen;
SetVars;
SetFields;
SetValidation(ValidateAtEnd);
MouseShow(true);
Result := EditForm(1);
GotoXY(1,25);
DisposeFields;
DisposeForms;
...
end.
Specify the field properties, and associate the field with a
variable.
Add labels to the fields.
Add the fields to the form and define their order and location
Initialize the variables that are going to be used in the form
Run the form.
Dispose of everything when the form is no longer needed.